#pragma once

#include "../Factory/StackFactory.h"

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	SLList  </Summary>
/// 
/// <purpose>	Single linked list	</purpose>
////////////////////////////////////////////////////////////////////////////////////////////////////

template <typename T>
class SLList
{
	struct Node : public IFactoryAble
	{
		T data;
		Node* next;
	};

	static StackFactory<Node> nodeFactory;

public:

	class Iterator
	{
		friend class SLList<T>;

		SLList<T>* list;

		typename SLList<T>::Node* prev;
		typename SLList<T>::Node* curr;

	public:

		Iterator(SLList<T>& listToIterate);

		void ChangeList(SLList<T>& listToIterate);

	public:

		void Begin();

		bool End();

		Iterator& operator++();

		T& operator*();
	};

private:

	unsigned int size;

	Node* head;

public:

	SLList();

	~SLList();

	SLList& operator=( const SLList& ref );

	SLList( const SLList& ref );

private:

	void RecursiveCopy( Node* node );

public:

	void Clear();

	void Push_Front( T item );

	T Pop_Front();

	void Insert(typename SLList<T>::Iterator& index, T item);

	void Erase(typename SLList<T>::Iterator& index);

	unsigned int GetSize() { return size; }
};

template<typename T>
StackFactory<typename SLList<T>::Node> SLList<T>::nodeFactory;






template<typename T>
SLList<T>::SLList() : head(0), size(0)
{

}

template<typename T>
SLList<T>::~SLList()
{
	Clear();
}

template<typename T>
SLList<T>::SLList( const SLList& ref ) : head(NULL), size(0)
{
	RecursiveCopy(ref.head);
}

template<typename T>
SLList<T>& SLList<T>::operator=( const SLList& ref )
{
	if(this != &ref)
	{
		Clear();
		RecursiveCopy(ref.head);
	}

	return *this;
}

template<typename T>
void SLList<T>::RecursiveCopy( Node* node )
{
	if(node)
	{
		RecursiveCopy(node->next);
		Push_Front(node->data);
	}
}

template<typename T>
void SLList<T>::Push_Front( T item )
{
	Node* temp = SLList::nodeFactory.NewObject();
	
	temp->data = item;
	temp->next = head;

	++size;

	head = temp;
}

template<typename T>
T SLList<T>::Pop_Front()
{
	Node* temp;

	temp = head;

	T ref = head->data;
	head = head->next;

	--size;

	SLList::nodeFactory.DeleteObject(temp);

	return ref;
}

template<typename T>
void SLList<T>::Clear()
{
	Node* temp;

	while(head)
	{
		temp = head;
		head = head->next;
		SLList::nodeFactory.DeleteObject(temp);
	}

	size = 0;
	head = 0;
}

template<typename T>
void SLList<T>::Insert(typename SLList<T>::Iterator& index, T item)
{
	if(index.prev)
	{
		Node* temp = SLList::nodeFactory.NewObject();
		temp->data = item;
		index.prev->next = temp;
		temp->next = index.curr;
		index.curr = temp;
		++size;
	}
	else if (index.curr)
	{
		Node* temp = SLList::nodeFactory.NewObject();
		temp->data = item;
		temp->next = head;
		head = temp;
		index.curr = head;
		++size;
	}
}

template<typename T>
void SLList<T>::Erase(typename SLList<T>::Iterator& index)
{
	if(index.prev && index.curr)
	{
		index.prev->next = index.curr->next;
		SLList::nodeFactory.DeleteObject(index.curr);
		index.curr = index.prev->next;
		--size;
	}
	else if (index.curr)
	{
		Node* temp = index.curr;
		index.curr = index.curr->next;
		head = index.curr;
		SLList::nodeFactory.DeleteObject(temp);
		--size;
	}
}













template<typename T>
SLList<T>::Iterator::Iterator(SLList<T>& listToIterate)
	: list(&listToIterate)
{
	Begin();
}

template<typename T>
void SLList<T>::Iterator::ChangeList(SLList<T>& listToIterate)
{
	list = &listToIterate;

	Begin();
}

template<typename T>
void SLList<T>::Iterator::Begin()
{
	curr = list->head;
	prev = 0;
}

template<typename T>
bool SLList<T>::Iterator::End()
{
	if(curr)
		return false;
	else
		return true;
}

template<typename T>
typename SLList<T>::Iterator& SLList<T>::Iterator::operator++()
{
	if(curr)
	{
		prev = curr;
		curr = curr->next;
	}

	return *this;
}

template<typename T>
T& SLList<T>::Iterator::operator*()
{
	return curr->data;
}